package com.jessechanrui.template.req18.pdf.itext;

import cn.com.agree.afa.svc.javaengine.AppLogger;
import cn.com.agree.afa.svc.javaengine.context.JavaDict;
import com.spire.pdf.FileFormat;
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.annotations.PdfRubberStampAnnotation;
import com.spire.pdf.annotations.appearance.PdfAppearance;
import com.spire.pdf.graphics.PdfImage;
import com.spire.pdf.graphics.PdfTemplate;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.io.FileUtils;
import org.docx4j.Docx4J;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import platform.consts.ConfigConsts;

import java.awt.geom.Rectangle2D;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author hexg 2022-12-14 15:30:00
 */
public class PdfUtils {

	private static Configuration config;

	private static Mapper fontMapper;

	private static Map<String, Template> TEMPLATES = new HashedMap<String, Template>();
	
	private static final String PREIX = "${";
	
	private static final char SUFFIX = '}';

	private static Mapper getFontMapper() {
		if (fontMapper == null) {
			synchronized (PdfUtils.class) {
				if (fontMapper == null) {
					fontMapper = new IdentityPlusMapper();
					fontMapper.put("华文楷体", PhysicalFonts.get("STKaiti"));
					fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
					fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
					fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
					fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
					fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
					fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
					fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
					fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
					fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
					fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
					fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
					fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
					fontMapper.put("等线", PhysicalFonts.get("SimSun"));
					fontMapper.put("等线 Light", PhysicalFonts.get("SimSun"));
					fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo"));
					fontMapper.put("华文隶书", PhysicalFonts.get("STLiti"));
					fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei"));
					fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun"));
					fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti"));
					fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi"));
					fontMapper.put("华文细黑", PhysicalFonts.get("STXihei"));
					fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
					fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
					fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));
					// 解决宋体（正文）和宋体（标题）的乱码问题
					PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));
					PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
				}
			}
		}
		return fontMapper;
	}

	/**
	 * 获取配置对象
	 *
	 * @return
	 * @throws IOException
	 */
	private static Configuration getConfiguration() throws IOException {
		if (config == null) {
			synchronized (PdfUtils.class) {
				if (config == null) {
					config = new Configuration(new Version("2.3.0"));
					URL url = PdfUtils.class.getClassLoader().getResource("template");
					AppLogger.info("template path is {}", url.getPath());
					config.setDirectoryForTemplateLoading(new File(url.getPath()));
					config.setDefaultEncoding(ConfigConsts.DEFAULT_CHARSET);
				}
			}
		}
		return config;
	}

	/**
	 * 合成数据与模板
	 *
	 * @param template 模板名称
	 * @param obj      模板需要填入的值
	 */
	public static String generate(String templateName, Object obj) throws IOException, TemplateException {
		Template template = TEMPLATES.get(templateName);
		String htmlStr = null;
		if (template == null) {
			Configuration config = getConfiguration();
			template = config.getTemplate(templateName, ConfigConsts.DEFAULT_CHARSET);
			TEMPLATES.put(templateName, template);
		}
		BufferedWriter writer = null;
		try {
			StringWriter stringWriter = new StringWriter();
			writer = new BufferedWriter(stringWriter);
			template.process(obj, writer);
			htmlStr = stringWriter.toString();
			writer.flush();
		} finally {
			if (writer != null) {
				writer.close();
			}
		}
		return htmlStr;
	}

	/**
	 * 根据模板生成word文档
	 *
	 * @param templateName   模板名称
	 * @param dataMap        模板需求数据
	 * @param outputFilePath 生成文件存放地址
	 * @throws IOException
	 * @throws TemplateException
	 */
	public static void generateWord(String templateName, Map<String, Object> dataMap, String outputFilePath)
			throws TemplateException, IOException {
		Writer out = null;
		try {
			// Configuration 用于读取ftl文件
			Configuration configuration = getConfiguration();
			// 输出文档路径及名称
			File outFile = new File(outputFilePath);
			Template template = configuration.getTemplate(templateName, ConfigConsts.DEFAULT_CHARSET);
			out = new BufferedWriter(
					new OutputStreamWriter(new FileOutputStream(outFile), ConfigConsts.DEFAULT_CHARSET), 10240);
			template.process(dataMap, out);
		} finally {
			if (out != null) {
				out.close();
			}
		}
	}

	/**
	 * 生成pdf文档
	 *
	 * @param templateName 模版文件
	 * @param obj          数据
	 * @param os           输出流
	 * @throws Exception
	 */
	public static void generatePdf(String templateName, Map<?, ?> dataMap, String pdfOutputFilePath) throws Exception {
		dataMap = checkAndGeneratePdf(templateName, dataMap);
		// 合并模板和数据模型 word doc os = ftl + obj
		String generate = generate(templateName, dataMap);

		try (ByteArrayInputStream in = new ByteArrayInputStream(generate.getBytes(ConfigConsts.DEFAULT_CHARSET));
				BufferedOutputStream outputStream = new BufferedOutputStream(
						new FileOutputStream(pdfOutputFilePath));) {

			// 文字处理对象
			WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(in);
			mlPackage.setFontMapper(getFontMapper());

			// wordMLPackage -> pdf os
			FOSettings foSettings = Docx4J.createFOSettings();
			foSettings.setWmlPackage(mlPackage);
			foSettings.setApacheFopMime(Docx4J.MIME_PDF);

			// 生成pdf
			Docx4J.toPDF(mlPackage, outputStream);
			outputStream.flush();
		} catch (Exception e) {
			AppLogger.error(e);
			throw e;
		}
	}
	
	/**
	 * 合法性检查，检查是否包含ftl参数，并将所有ftl需要参数构造在一起
	 * @param ftlPath  ftl模板位置
	 * @param dataMap 参数存储的map，可能存在ftl不需要的key
	 * @return
	 * @throws Exception 
	 */
	public static JavaDict checkAndGeneratePdf(String templateName, Map<?, ?> dataMap) throws Exception {
		String path = PdfUtils.class.getClassLoader().getResource("template").getPath();
		File ftlFile = new File(path,templateName);
		if(!ftlFile.exists()) {
			AppLogger.error("模板文件路径{}不存在，请检查",ftlFile.getAbsoluteFile());
			throw new Exception("模板文件不存在");
		}
		String str = FileUtils.readFileToString(ftlFile, Charset.defaultCharset());
		String preix = PREIX;
		int index =-1;
		int length = str.length();
		List<String> list = new ArrayList<>();
		while((index = str.indexOf(preix, index+1) ) != -1) {
			index +=2;
			StringBuilder key = new StringBuilder();
			while(index < length && str.charAt(index) !=SUFFIX) {
				key.append(str.charAt(index));
				index++;
			}
			list.add(key.toString());
			
		}
		JavaDict paramDict = new JavaDict();
		for(String key : list) {
			if(dataMap.containsKey(key)) {
				Object value = dataMap.get(key);
				if(!(value instanceof String)) {
					throw new Exception( key + " 参数在参数集合中对应的参数值类型错误，当前参数类型是" + value.getClass().getSimpleName() + ",而不是string");
				}
				paramDict.put(key, value);
			}else {
				throw new Exception( key + " 参数在参数集合中找不到，请检查");
			}
		}
		AppLogger.info("参数集合构造成功，集合值如下：");
		AppLogger.info(paramDict);
		return paramDict;
	}

	/**
	 * 给pdf添加图片(盖章功能) 使用spire.pdf实现
	 * 
	 * @param imagePath         添加的图片位置
	 * @param sourcePdfFilePath 源文件路径
	 * @param targetPdfFilePath 目标文件路径
	 * @param imageX            图片在pdf中的x轴
	 * @param imageY            图片在pdf中的y轴
	 * @throws IOException
	 */
	public static void addImageToPdf(String imagePath, String sourcePdfFilePath, String targetPdfFilePath, int imageX,
			int imageY) throws IOException {
		// 假如图片的x、y轴的路径均为负数，使用默认的x，y轴
		if (imageY < 0 && imageX < 0) {
			imageX = 350;
			imageY = 200;
		}
		PdfDocument doc = null;
		try {
			doc = new PdfDocument();
			doc.loadFromFile(sourcePdfFilePath);
			// 获取加盖印章的页面
			PdfPageBase page = doc.getPages().get(0);
			// 加载印章图片
			PdfImage image = PdfImage.fromFile(imagePath);
			int width = image.getWidth();
			int height = image.getHeight();
			PdfTemplate template = new PdfTemplate(width, height);
			// 将图片绘制到模板
			template.getGraphics().drawImage(image, 0, 0, width, height);
			Rectangle2D rect = new Rectangle2D.Float(imageX, imageY, width, height);
			PdfRubberStampAnnotation stamp = new PdfRubberStampAnnotation(rect);
			PdfAppearance pdfAppearance = new PdfAppearance(stamp);
			// 将模板应用为pdfAppearance的一般状态
			pdfAppearance.setNormal(template);
			stamp.setAppearance(pdfAppearance);
			page.getAnnotationsWidget().add(stamp);
			doc.saveToFile(targetPdfFilePath, FileFormat.PDF);
			// 删除源文件
			new File(sourcePdfFilePath).delete();
		} catch (Exception e) {
			AppLogger.error(e);
			throw new IOException("PDF添加印章异常");
		} finally {
			if (doc != null) {
				doc.close();
			}
		}
	}
	
	/**
	 * 给pdf添加图片(盖章功能) 使用spire.pdf实现
	 * 
	 * @param imagePath         添加的图片位置
	 * @param sourcePdfFilePath 源文件路径
	 * @param targetPdfFilePath 目标文件路径
	 * @param imageX            图片在pdf中的x轴
	 * @param imageY            图片在pdf中的y轴
	 * @param width             印章宽(mm)
	 * @param height            印章长(mm)
	 * @throws IOException
	 */
	public static void addImageToPdf(String imagePath, String sourcePdfFilePath, String targetPdfFilePath, int imageX,
			int imageY, int width, int height) throws IOException {
		// 假如图片的x、y轴的路径均为负数，使用默认的x，y轴
		if (imageY < 0 && imageX < 0) {
			imageX = 350;
			imageY = 200;
		}
		if (width < 0 && height < 0) {
			width = 40;
			height = 40;
		}
		PdfDocument doc = null;
		try {
			doc = new PdfDocument();
			doc.loadFromFile(sourcePdfFilePath);
			// 获取加盖印章的页面
			PdfPageBase page = doc.getPages().get(0);
			// 加载印章图片
			PdfImage image = PdfImage.fromFile(imagePath);
			// 每毫米像素
			float dpmm = 72f / 25.4f;
			// 印章长宽像素
			float widthpx = width * dpmm;
			float heightpx = height * dpmm;
			PdfTemplate template = new PdfTemplate(widthpx, heightpx);
			// 将图片绘制到模板
			template.getGraphics().drawImage(image, 0, 0, widthpx, heightpx);
			Rectangle2D rect = new Rectangle2D.Float(imageX, imageY, widthpx, heightpx);
			PdfRubberStampAnnotation stamp = new PdfRubberStampAnnotation(rect);
			PdfAppearance pdfAppearance = new PdfAppearance(stamp);
			// 将模板应用为pdfAppearance的一般状态
			pdfAppearance.setNormal(template);
			stamp.setAppearance(pdfAppearance);
			page.getAnnotationsWidget().add(stamp);
			doc.saveToFile(targetPdfFilePath, FileFormat.PDF);
			// 删除源文件
			new File(sourcePdfFilePath).delete();
		} catch (Exception e) {
			AppLogger.error(e);
			throw new IOException("PDF添加印章异常");
		} finally {
			if (doc != null) {
				doc.close();
			}
		}
	}

	/**
	 * 给pdf添加背景图片
	 * 
	 * @param imagePath
	 * @param sourcePdfFilePath
	 * @param targetPdfFilePath
	 * @param imageX
	 * @param imageY
	 */
	public static void addBackGroudImage(String imagePath, String sourcePdfFilePath, String targetPdfFilePath,
			int imageX, int imageY) {
		// 假如图片的x、y轴的路径均为负数，使用默认的x，y轴
		if (imageY < 0 && imageX < 0) {
			imageX = 350;
			imageY = 200;
		}
		PdfDocument doc = new PdfDocument();
		doc.loadFromFile(sourcePdfFilePath);
		// 获取加盖印章的页面
		PdfPageBase page = doc.getPages().get(0);
		// 设置背景图片
		page.setBackgroundImage(imagePath);

		// 设置背景区域
		Rectangle2D rect = new Rectangle2D.Float(imageX, imageY, 150, 150);
		page.setBackgroundRegion(rect);
		doc.saveToFile(targetPdfFilePath, FileFormat.PDF);
		doc.close();
		new File(sourcePdfFilePath).delete();
	}

//	private static void newImageAnchor(WordprocessingMLPackage wordMLPackage, String imagePath) throws Exception {
//		Document document = wordMLPackage.getMainDocumentPart().getContents();
//		Body body = document.getBody();
//
//		RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");
//		new TraversalUtil(body.getContent(), rt);
//
//		List<CTBookmark> bookmarks = rt.getStarts();
//		for (CTBookmark bookmark : bookmarks) {
//			if ("sealImage".equals(bookmark.getName())) {
//				addImage(wordMLPackage, bookmark, imagePath);
//				bookmarks.remove(bookmark);
//				break;
//			}
//		}
//	}

//	/**
//	 * @param wordMLPackage
//	 * @param bookmark
//	 * @throws Exception
//	 */
//	private static void addImage(WordprocessingMLPackage wordMLPackage, CTBookmark bookmark, String imagePath)
//			throws Exception {
//		byte[] bytes = FileUtils.readFileToByteArray(new File(imagePath));
//		BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
//		Anchor inline = imagePart.createImageAnchor(null, null, 0, 1, false, 100, 200);
//		// Now add the inline in w:p/w:r/w:drawing
//		org.docx4j.wml.ObjectFactory factory = Context.getWmlObjectFactory();
//		org.docx4j.wml.P p = (P) bookmark.getParent();
//		org.docx4j.wml.R run = factory.createR();
//		p.getContent().add(run);
//		org.docx4j.wml.Drawing drawing = factory.createDrawing();
//		run.getContent().add(drawing);
//		drawing.getAnchorOrInline().add(inline);
//	}

//	/**
//	 * 给pdf添加图片(盖章功能) 使用iText实现
//	 * 
//	 * @param imagePath         添加的图片位置
//	 * @param sourcePdfFilePath 源文件路径
//	 * @param targetPdfFilePath 目标文件路径
//	 * @param imageX            图片在pdf中的x轴
//	 * @param imageY            图片在pdf中的y轴
//	 */
//	public static void addImageToPdf0(String imagePath, String sourcePdfFilePath, String targetPdfFilePath, int imageX,
//			int imageY) throws Exception {
//		// 假如图片的x、y轴的路径均为负数，使用默认的x，y轴
//		if (imageY < 0 && imageX < 0) {
//			imageX = 380;
//			imageY = 500;
//		}
//		PdfReader reader = null;
//		PdfStamper stamp = null;
//		try {
//			// 要添加水印的原pdf文件路径
//			reader = new PdfReader(sourcePdfFilePath, "PDF".getBytes());
//			// 加了水印的pdf路径
//			stamp = new PdfStamper(reader, new FileOutputStream(targetPdfFilePath));
//			// 插入水印
//			Image img = Image.getInstance(imagePath);
//			// 设置印章位置
//			img.setAbsolutePosition(imageX, imageY);
//			// 设置印章大小
//			img.scalePercent(50);
//			// 原pdf文件的总页数
//			int pageSize = reader.getNumberOfPages();
//			for (int i = 1; i <= pageSize; i++) {
//				PdfContentByte under = stamp.getUnderContent(i);
//				// 往pdf上添加图片（水印）
//				under.addImage(img);
//			}
//			stamp.flush();
//		} catch (IOException | DocumentException e) {
//			AppLogger.error(e);
//			throw new Exception(e);
//		} finally {
//			if (stamp != null) {
//				stamp.close();
//			}
//			if (reader != null) {
//				reader.close();
//			}
//		}
//	}

//	    /**
//	     * 图片转码
//	     */
//	    public static String getImageStr(String imgpath) {
//	        InputStream in = null;
//	        byte[] data = null;
//	        try {
//	            in = new FileInputStream(imgpath);
//	            data = new byte[in.available()];
//	            in.read(data);
//	            in.close();
//	        } catch (Exception e) {
//	            e.printStackTrace();
//	        }
//	        BASE64Encoder encoder = new BASE64Encoder();
//	        return encoder.encode(data);
//	    }

//	public static void main(String[] args) throws Exception {
//		ExecutorService executors = Executors.newFixedThreadPool(20);
//		String templateName = "DJSB.ftl";
//		JavaDict pdfParams = new JavaDict();
//		Date date = new Date();
//		DateFormat format = new SimpleDateFormat("yyyy年MM月dd日HH时mm分");
//		pdfParams.put("date", format.format(date));
//		pdfParams.put("freezeOrgName", "zzx");
//		pdfParams.put("docNo", "001");
//		pdfParams.put("acctCardNo", "32323232");
//		pdfParams.put("name", "3232323");
//		pdfParams.put("errorMsg", "交易失败");
//		String imagePath = "E:\\code\\IBS_MOD\\ibs_jip\\workspace\\IBS_JIP\\IBSJIPMODEL\\afa\\config\\custom\\image\\seal.png";
//		String pdfOutputFilePath = "E:/temp.pdf";
//		generatePdf(templateName, pdfParams, pdfOutputFilePath);
//		generatePdf(templateName, pdfParams, pdfOutputFilePath, imagePath);
//		AtomicInteger inter = new AtomicInteger();
//		for (int i = 0; i < 10000; i++) {
//			Runnable runnable = new Runnable() {
//
//				@Override
//				public void run() {
//					try {
//						int num = inter.addAndGet(1);
//						String pdfOutputFilePath = "E:/temp" + num + ".pdf";
//						generatePdf(templateName, pdfParams, pdfOutputFilePath);
//						String targetPdfFilePath = "E:/target" + num + ".pdf";
//						addImageToPdf(imagePath, pdfOutputFilePath, targetPdfFilePath, -1, -1);
//						System.out.println("=========================" + num);
//					} catch (Exception e) {
//						e.printStackTrace();
//					}
//
//				}
//			};
//			executors.submit(runnable);
//		}

//	}

}
